package com.example.mq.mqserver.core;

import com.example.mq.common.MqException;

//实现交换机的转发规则 和验证routingKey是否合法
public class Router {
    //bindingKey构造规则
    //数字字母下划线
    //使用.分割
    //允许*和#
    public boolean checkBindingKey(String bindingKey) {
        //todo
        if (bindingKey.length() == 0) {
            //空字符串 也是合法情况
            return true;
        }
        //检查字符串中不能存在非法字符
        for (int i = 0; i < bindingKey.length(); i++) {
            char ch = bindingKey.charAt(i);
            if (ch >= 'A' && ch <= 'Z') {
                continue;
            }
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            if (ch == '_' || ch == '.' || ch == '*' || ch == '#') {
                continue;
            }
            return false;
        }
        //检查*或者#是否是独立的部分
        String[] words = bindingKey.split("\\.");
        for (String word : words) {
            //检查word长度 >1 且包含#或*就不合法
            if (word.length() > 1 && word.contains("*") || word.contains("#")) {
                return false;
            }
        }
        //约定通配符之间的相邻关系
        //形如这种 aaa.#.#.bbb aaa.*.#.bbb aaa.#.*.bbb非法
        //aaa.*.*.bbb合法
        for (int i = 0; i < words.length - 1; i++) {
            //是否为连续两个#
            if (words[i].equals("#") && words[i + 1].equals("#")) {
                return false;
            }
            //#连着*
            if (words[i].equals("#") && words[i + 1].equals("*")) {
                return false;
            }
            //*连着#
            if (words[i].equals("*") && words[i + 1].equals("#")) {
                return false;
            }
        }
        return true;
    }

    //数字字母下划线
    //使用.分割
    public boolean checkRoutingKey(String routingKey) {
        if (routingKey.length() == 0) {
            //空字符串 合法 在使用fanout交换机时 routingKey用不上
            return true;
        }
        for (int i = 0; i < routingKey.length(); i++) {
            char ch = routingKey.charAt(i);
            //判断该字符是否是大写字母
            if (ch >= 'A' && ch <= 'Z') {
                continue;
            }
            //判断该字母是否是小写字母
            if (ch >= 'a' && ch <= 'z') {
                continue;
            }
            //判断该字母是否是阿拉伯数字
            if (ch >= '0' && ch <= '9') {
                continue;
            }
            //判定是否是_或者.
            if (ch == '_' || ch == '.') {
                continue;
            }
            //该字符不满足任何一种情况 就返回false
            return false;
        }
        return true;
    }

    //这个方法用来判定该消息是否可以转发给这个绑定对应的队列
    public boolean route(ExchangeType exchangeType, Binding binding, Message message) throws MqException {
        //TODO
        //根据不同的exchangeType使用不同的转发规则
        if (exchangeType == ExchangeType.FANOUT) {
            //所有都转发 直接返回true;
            return true;
        } else if (exchangeType == ExchangeType.TOPIC) {
            return routeTopic(binding, message);
        } else {
            //不应该存在这种情况
            throw new MqException("[Router] 交换机类型非法 exchangeType = " + exchangeType);
        }
    }

    private boolean routeTopic(Binding binding, Message message) {
        //先把这两个key进行切分
        String[] bindingTokens = binding.getBindingKey().split("\\.");
        String[] routingTokens = message.getRoutingKey().split("\\.");
        int bindingIndex = 0;
        int routingIndex = 0;
        //此处使用while
        while (bindingIndex < bindingTokens.length && routingIndex < routingTokens.length) {
            //如果遇到* 直接进入下一轮 *可以匹配到任意一个部分
            if (bindingTokens[bindingIndex].equals("*")) {
                bindingIndex++;
                routingIndex++;
                continue;
            } else if(bindingTokens[bindingIndex].equals("#")){
                //如果遇到#号 要先看看有没有下一个位置
                bindingIndex++;
                if(bindingIndex == bindingTokens.length){
                    //该#后面没有东西 是最后一个字符
                    return true;
                }
                //后面还有东西
                //拿着这个内容 在routingKey中往后找 找到对应的位置
                //findNextMatch 这个方法用来查找该部分 在routingKey中的位置 返回该下标 没找到返回-1
                routingIndex = findNextMatch(routingTokens,routingIndex,bindingTokens[bindingIndex]);
                if(routingIndex == -1){
                    return false;
                }
                //找到的匹配的情况 就继续往后匹配
                bindingIndex++;
                routingIndex++;
            }else {
                //如果遇到普通字符串
                if(!bindingTokens[bindingIndex].equals(routingTokens[routingIndex])){
                    return false;
                }
                bindingIndex++;
                routingIndex++;
            }
        }
        //判定是否是双方到达末尾
        //比如 aaa.bbb.ccc和aaa.bbb是要匹配失败的
        if (bindingIndex == bindingTokens.length && routingIndex == routingTokens.length){
            return true;
        }
        return false;
    }

    private int findNextMatch(String[] routingTokens, int routingIndex, String bindingToken) {
        for (int i = routingIndex; i < routingTokens.length; i++) {
            if(routingTokens[i].equals(bindingToken)){
                return i;
            }
        }
        return -1;
    }

}
